home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2000 / MacHack 2000.toast / pc / The Hacks / MacHacksBug / Python 1.5.2c1 / Mac / Lib / FrameWork.py < prev    next >
Encoding:
Python Source  |  2000-06-23  |  27.8 KB  |  1,042 lines

  1. "A sort of application framework for the Mac"
  2.  
  3. DEBUG=0
  4.  
  5. import MacOS
  6. import traceback
  7.  
  8. from AE import *
  9. from AppleEvents import *
  10. from Ctl import *
  11. from Controls import *
  12. from Dlg import *
  13. from Dialogs import *
  14. from Evt import *
  15. from Events import *
  16. from Menu import *
  17. from Menus import *
  18. from Qd import *
  19. from QuickDraw import *
  20. #from Res import *
  21. #from Resources import *
  22. #from Snd import *
  23. #from Sound import *
  24. from Win import *
  25. from Windows import *
  26. import types
  27.  
  28. import EasyDialogs
  29.  
  30. kHighLevelEvent = 23    # Don't know what header file this should come from
  31. SCROLLBARWIDTH = 16        # Again, not a clue...
  32.  
  33.  
  34. # Map event 'what' field to strings
  35. eventname = {}
  36. eventname[1] = 'mouseDown'
  37. eventname[2] = 'mouseUp'
  38. eventname[3] = 'keyDown'
  39. eventname[4] = 'keyUp'
  40. eventname[5] = 'autoKey'
  41. eventname[6] = 'updateEvt'
  42. eventname[7] = 'diskEvt'
  43. eventname[8] = 'activateEvt'
  44. eventname[15] = 'osEvt'
  45. eventname[23] = 'kHighLevelEvent'
  46.  
  47. # Map part codes returned by WhichWindow() to strings
  48. partname = {}
  49. partname[0] = 'inDesk'
  50. partname[1] = 'inMenuBar'
  51. partname[2] = 'inSysWindow'
  52. partname[3] = 'inContent'
  53. partname[4] = 'inDrag'
  54. partname[5] = 'inGrow'
  55. partname[6] = 'inGoAway'
  56. partname[7] = 'inZoomIn'
  57. partname[8] = 'inZoomOut'
  58.  
  59. #
  60. # The useable portion of the screen
  61. #    ## but what happens with multiple screens? jvr
  62. screenbounds = qd.screenBits.bounds
  63. screenbounds = screenbounds[0]+4, screenbounds[1]+4, \
  64.     screenbounds[2]-4, screenbounds[3]-4
  65.     
  66. next_window_x = 16        # jvr
  67. next_window_y = 44        # jvr
  68.  
  69. def windowbounds(width, height):
  70.     "Return sensible window bounds"
  71.     global next_window_x, next_window_y
  72.     r, b = next_window_x+width, next_window_y+height
  73.     if r > screenbounds[2]:
  74.         next_window_x = 16
  75.     if b > screenbounds[3]:
  76.         next_window_y = 44
  77.     l, t = next_window_x, next_window_y
  78.     r, b = next_window_x+width, next_window_y+height
  79.     next_window_x, next_window_y = next_window_x + 8, next_window_y + 20    # jvr
  80.     return l, t, r, b
  81.  
  82. _watch = None
  83. def setwatchcursor():
  84.     global _watch
  85.     
  86.     if _watch == None:
  87.         _watch = GetCursor(4).data
  88.     SetCursor(_watch)
  89.     
  90. def setarrowcursor():
  91.     SetCursor(qd.arrow)
  92.  
  93. class Application:
  94.     
  95.     "Application framework -- your application should be a derived class"
  96.     
  97.     def __init__(self, nomenubar=0):
  98.         self._doing_asyncevents = 0
  99.         self.quitting = 0
  100.         self.needmenubarredraw = 0
  101.         self._windows = {}
  102.         if nomenubar:
  103.             self.menubar = None
  104.         else:
  105.             self.makemenubar()
  106.             
  107.     def __del__(self):
  108.         if self._doing_asyncevents:
  109.             self._doing_asyncevents = 0
  110.             MacOS.SetEventHandler()
  111.     
  112.     def makemenubar(self):
  113.         self.menubar = MenuBar(self)
  114.         AppleMenu(self.menubar, self.getabouttext(), self.do_about)
  115.         self.makeusermenus()
  116.  
  117.     def makeusermenus(self):
  118.         self.filemenu = m = Menu(self.menubar, "File")
  119.         self._quititem = MenuItem(m, "Quit", "Q", self._quit)
  120.     
  121.     def _quit(self, *args):
  122.         self.quitting = 1
  123.         
  124.     def cleanup(self):
  125.         for w in self._windows.values():
  126.             w.do_close()
  127.         return self._windows == {}
  128.         
  129.     def appendwindow(self, wid, window):
  130.         self._windows[wid] = window
  131.         
  132.     def removewindow(self, wid):
  133.         del self._windows[wid]
  134.     
  135.     def getabouttext(self):
  136.         return "About %s..." % self.__class__.__name__
  137.     
  138.     def do_about(self, id, item, window, event):
  139.         EasyDialogs.Message("Hello, world!" + "\015(%s)" % self.__class__.__name__)
  140.     
  141.     # The main event loop is broken up in several simple steps.
  142.     # This is done so you can override each individual part,
  143.     # if you have a need to do extra processing independent of the
  144.     # event type.
  145.     # Normally, however, you'd just define handlers for individual
  146.     # events.
  147.     # (XXX I'm not sure if using default parameter values is the right
  148.     # way to define the mask and wait time passed to WaitNextEvent.)
  149.     
  150.     def mainloop(self, mask = everyEvent, wait = 0):
  151.         self.quitting = 0
  152.         saveparams = apply(MacOS.SchedParams, self.schedparams)
  153.         try:
  154.             while not self.quitting:
  155.                 try:
  156.                     self.do1event(mask, wait)
  157.                 except (Application, SystemExit):
  158.                     # Note: the raising of "self" is old-fashioned idiom to
  159.                     # exit the mainloop. Calling _quit() is better for new
  160.                     # applications.
  161.                     break
  162.         finally:
  163.             apply(MacOS.SchedParams, self.schedparams)
  164.     
  165.     schedparams = MacOS.SchedParams()
  166.     
  167.     def dopendingevents(self, mask = everyEvent):
  168.         """dopendingevents - Handle all pending events"""
  169.         while self.do1event(mask, wait=0):
  170.             pass
  171.     
  172.     def do1event(self, mask = everyEvent, wait = 0):
  173.         ok, event = self.getevent(mask, wait)
  174.         if IsDialogEvent(event):
  175.             if self.do_dialogevent(event):
  176.                 return
  177.         if ok:
  178.             self.dispatch(event)
  179.         else:
  180.             self.idle(event)
  181.             
  182.     def idle(self, event):
  183.         pass
  184.     
  185.     def getevent(self, mask = everyEvent, wait = 0):
  186.         if self.needmenubarredraw:
  187.             DrawMenuBar()
  188.             self.needmenubarredraw = 0
  189.         ok, event = WaitNextEvent(mask, wait)
  190.         return ok, event
  191.             
  192.     def dispatch(self, event):
  193.         # The following appears to be double work (already done in do1event)
  194.         # but we need it for asynchronous event handling
  195.         if IsDialogEvent(event):
  196.             if self.do_dialogevent(event):
  197.                 return
  198.         (what, message, when, where, modifiers) = event
  199.         if eventname.has_key(what):
  200.             name = "do_" + eventname[what]
  201.         else:
  202.             name = "do_%d" % what
  203.         try:
  204.             handler = getattr(self, name)
  205.         except AttributeError:
  206.             handler = self.do_unknownevent
  207.         handler(event)
  208.         
  209.     def asyncevents(self, onoff):
  210.         """asyncevents - Set asynchronous event handling on or off"""
  211.         old = self._doing_asyncevents
  212.         if old:
  213.             MacOS.SetEventHandler()
  214.             apply(MacOS.SchedParams, self.schedparams)
  215.         if onoff:
  216.             MacOS.SetEventHandler(self.dispatch)
  217.             doint, dummymask, benice, howoften, bgyield = \
  218.                    self.schedparams
  219.             MacOS.SchedParams(doint, everyEvent, benice,
  220.                       howoften, bgyield)
  221.         self._doing_asyncevents = onoff
  222.         return old
  223.             
  224.     def do_dialogevent(self, event):
  225.         gotone, window, item = DialogSelect(event)
  226.         if gotone:
  227.             if self._windows.has_key(window):
  228.                 self._windows[window].do_itemhit(item, event)
  229.             else:
  230.                 print 'Dialog event for unknown dialog'
  231.             return 1
  232.         return 0
  233.     
  234.     def do_mouseDown(self, event):
  235.         (what, message, when, where, modifiers) = event
  236.         partcode, wid = FindWindow(where)
  237.  
  238.         #
  239.         # Find the correct name.
  240.         #
  241.         if partname.has_key(partcode):
  242.             name = "do_" + partname[partcode]
  243.         else:
  244.             name = "do_%d" % partcode
  245.  
  246.         if wid == None:
  247.             # No window, or a non-python window    
  248.             try:
  249.                 handler = getattr(self, name)
  250.             except AttributeError:
  251.                 # Not menubar or something, so assume someone
  252.                 # else's window
  253.                 MacOS.HandleEvent(event)
  254.                 return        
  255.         elif self._windows.has_key(wid):
  256.             # It is a window. Hand off to correct window.
  257.             window = self._windows[wid]
  258.             try:
  259.                 handler = getattr(window, name)
  260.             except AttributeError:
  261.                 handler = self.do_unknownpartcode
  262.         else:
  263.             # It is a python-toolbox window, but not ours.
  264.             handler = self.do_unknownwindow
  265.         handler(partcode, wid, event)
  266.  
  267.     def do_inSysWindow(self, partcode, window, event):
  268.         MacOS.HandleEvent(event)
  269.     
  270.     def do_inDesk(self, partcode, window, event):
  271.         MacOS.HandleEvent(event)
  272.     
  273.     def do_inMenuBar(self, partcode, window, event):
  274.         if not self.menubar:
  275.             MacOS.HandleEvent(event)
  276.             return
  277.         (what, message, when, where, modifiers) = event
  278.         result = MenuSelect(where)
  279.         id = (result>>16) & 0xffff    # Hi word
  280.         item = result & 0xffff        # Lo word
  281.         self.do_rawmenu(id, item, window, event)
  282.     
  283.     def do_rawmenu(self, id, item, window, event):
  284.         try:
  285.             self.do_menu(id, item, window, event)
  286.         finally:
  287.             HiliteMenu(0)
  288.     
  289.     def do_menu(self, id, item, window, event):
  290.         self.menubar.dispatch(id, item, window, event)
  291.     
  292.     
  293.     def do_unknownpartcode(self, partcode, window, event):
  294.         (what, message, when, where, modifiers) = event
  295.         if DEBUG: print "Mouse down at global:", where
  296.         if DEBUG: print "\tUnknown part code:", partcode
  297.         if DEBUG: print "\tEvent:", self.printevent(event)
  298.         MacOS.HandleEvent(event)
  299.         
  300.     def do_unknownwindow(self, partcode, window, event):
  301.         if DEBUG: print 'Unknown window:', window
  302.         MacOS.HandleEvent(event)
  303.     
  304.     def do_keyDown(self, event):
  305.         self.do_key(event)
  306.     
  307.     def do_autoKey(self, event):
  308.         if not event[-1] & cmdKey:
  309.             self.do_key(event)
  310.     
  311.     def do_key(self, event):
  312.         (what, message, when, where, modifiers) = event
  313.         c = chr(message & charCodeMask)
  314.         if modifiers & cmdKey:
  315.             if c == '.':
  316.                 raise self
  317.             else:
  318.                 if not self.menubar:
  319.                     MacOS.HandleEvent(event)
  320.                     return
  321.                 result = MenuKey(ord(c))
  322.                 id = (result>>16) & 0xffff    # Hi word
  323.                 item = result & 0xffff        # Lo word
  324.                 if id:
  325.                     self.do_rawmenu(id, item, None, event)
  326. #                elif c == 'w':
  327. #                    w = FrontWindow()
  328. #                    if w:
  329. #                        self.do_close(w)
  330. #                    else:
  331. #                        if DEBUG: print 'Command-W without front window'
  332.                 else:
  333.                     if DEBUG: print "Command-" +`c`
  334.         else:
  335.             # See whether the front window wants it
  336.             w = FrontWindow()
  337.             if w and self._windows.has_key(w):
  338.                 window = self._windows[w]
  339.                 try:
  340.                     do_char = window.do_char
  341.                 except AttributeError:
  342.                     do_char = self.do_char
  343.                 do_char(c, event)
  344.             # else it wasn't for us, sigh...
  345.     
  346.     def do_char(self, c, event):
  347.         if DEBUG: print "Character", `c`
  348.     
  349.     def do_updateEvt(self, event):
  350.         (what, message, when, where, modifiers) = event
  351.         wid = WhichWindow(message)
  352.         if wid and self._windows.has_key(wid):
  353.             window = self._windows[wid]
  354.             window.do_rawupdate(wid, event)
  355.         else:
  356.             MacOS.HandleEvent(event)
  357.     
  358.     def do_activateEvt(self, event):
  359.         (what, message, when, where, modifiers) = event
  360.         wid = WhichWindow(message)
  361.         if wid and self._windows.has_key(wid):
  362.             window = self._windows[wid]
  363.             window.do_activate(modifiers & 1, event)
  364.         else:
  365.             MacOS.HandleEvent(event)
  366.     
  367.     def do_osEvt(self, event):
  368.         (what, message, when, where, modifiers) = event
  369.         which = (message >> 24) & 0xff
  370.         if which == 1:    # suspend/resume
  371.             self.do_suspendresume(event)
  372.         else:
  373.             if DEBUG:
  374.                 print 'unknown osEvt:',
  375.                 self.printevent(event)
  376.     
  377.     def do_suspendresume(self, event):
  378.         (what, message, when, where, modifiers) = event
  379.         wid = FrontWindow()
  380.         if wid and self._windows.has_key(wid):
  381.             window = self._windows[wid]
  382.             window.do_activate(modifiers & 1, event)
  383.     
  384.     def do_kHighLevelEvent(self, event):
  385.         (what, message, when, where, modifiers) = event
  386.         if DEBUG: 
  387.             print "High Level Event:",
  388.             self.printevent(event)
  389.         try:
  390.             AEProcessAppleEvent(event)
  391.         except:
  392.             print "AEProcessAppleEvent error:"
  393.             traceback.print_exc()
  394.     
  395.     def do_unknownevent(self, event):
  396.         if DEBUG:
  397.             print "Unhandled event:",
  398.             self.printevent(event)
  399.     
  400.     def printevent(self, event):
  401.         (what, message, when, where, modifiers) = event
  402.         nicewhat = `what`
  403.         if eventname.has_key(what):
  404.             nicewhat = eventname[what]
  405.         print nicewhat,
  406.         if what == kHighLevelEvent:
  407.             h, v = where
  408.             print `ostypecode(message)`, hex(when), `ostypecode(h | (v<<16))`,
  409.         else:
  410.             print hex(message), hex(when), where,
  411.         print hex(modifiers)
  412.  
  413.  
  414. class MenuBar:
  415.     """Represent a set of menus in a menu bar.
  416.     
  417.     Interface:
  418.     
  419.     - (constructor)
  420.     - (destructor)
  421.     - addmenu
  422.     - addpopup (normally used internally)
  423.     - dispatch (called from Application)
  424.     """
  425.     
  426.     nextid = 1    # Necessarily a class variable
  427.     
  428.     def getnextid(self):
  429.         id = MenuBar.nextid
  430.         MenuBar.nextid = id+1
  431.         return id
  432.     
  433.     def __init__(self, parent=None):
  434.         self.parent = parent
  435.         ClearMenuBar()
  436.         self.bar = GetMenuBar()
  437.         self.menus = {}
  438.     
  439.     # XXX necessary?
  440.     def close(self):
  441.         self.parent = None
  442.         self.bar = None
  443.         self.menus = None
  444.     
  445.     def addmenu(self, title, after = 0):
  446.         id = self.getnextid()
  447.         if DEBUG: print 'Newmenu', title, id # XXXX
  448.         m = NewMenu(id, title)
  449.         m.InsertMenu(after)
  450.         if after >= 0:
  451.             if self.parent:
  452.                 self.parent.needmenubarredraw = 1
  453.             else:
  454.                 DrawMenuBar()
  455.         return id, m
  456.         
  457.     def delmenu(self, id):
  458.         if DEBUG: print 'Delmenu', id # XXXX
  459.         DeleteMenu(id)
  460.     
  461.     def addpopup(self, title = ''):
  462.         return self.addmenu(title, -1)
  463.  
  464. # Useless:    
  465. #    def install(self):
  466. #        if not self.bar: return
  467. #        SetMenuBar(self.bar)
  468. #        if self.parent:
  469. #            self.parent.needmenubarredraw = 1
  470. #        else:
  471. #            DrawMenuBar()
  472.     
  473.     def fixmenudimstate(self):
  474.         for m in self.menus.keys():
  475.             menu = self.menus[m]
  476.             if menu.__class__ == FrameWork.AppleMenu:
  477.                 continue
  478.             for i in range(len(menu.items)):
  479.                 label, shortcut, callback, kind = menu.items[i]
  480.                 if type(callback) == types.StringType:
  481.                     wid = Win.FrontWindow()
  482.                     if wid and self.parent._windows.has_key(wid):
  483.                         window = self.parent._windows[wid]
  484.                         if hasattr(window, "domenu_" + callback):
  485.                             menu.menu.EnableItem(i + 1)
  486.                         elif hasattr(self.parent, "domenu_" + callback):
  487.                             menu.menu.EnableItem(i + 1)
  488.                         else:
  489.                             menu.menu.DisableItem(i + 1)
  490.                     elif hasattr(self.parent, "domenu_" + callback):
  491.                         menu.menu.EnableItem(i + 1)
  492.                     else:
  493.                         menu.menu.DisableItem(i + 1)
  494.                 elif callback:
  495.                     pass
  496.                     
  497.     def dispatch(self, id, item, window, event):
  498.         if self.menus.has_key(id):
  499.             self.menus[id].dispatch(id, item, window, event)
  500.         else:
  501.             if DEBUG: print "MenuBar.dispatch(%d, %d, %s, %s)" % \
  502.                 (id, item, window, event)
  503.     
  504.  
  505. # XXX Need a way to get menus as resources and bind them to callbacks
  506.  
  507. class Menu:
  508.     "One menu."
  509.     
  510.     def __init__(self, bar, title, after=0):
  511.         self.bar = bar
  512.         self.id, self.menu = self.bar.addmenu(title, after)
  513.         bar.menus[self.id] = self
  514.         self.items = []
  515.         self._parent = None
  516.         
  517.     def delete(self):
  518.         self.bar.delmenu(self.id)
  519.         del self.bar.menus[self.id]
  520.         del self.bar
  521.         del self.items
  522.         del self.menu
  523.         del self.id
  524.         del self._parent
  525.         
  526.     def additem(self, label, shortcut=None, callback=None, kind=None):
  527.         self.menu.AppendMenu('x')        # add a dummy string
  528.         self.items.append(label, shortcut, callback, kind)
  529.         item = len(self.items)
  530.         self.menu.SetMenuItemText(item, label)        # set the actual text
  531.         if shortcut:
  532.             self.menu.SetItemCmd(item, ord(shortcut))
  533.         return item
  534.         
  535.     def delitem(self, item):
  536.         if item != len(self.items):
  537.             raise 'Can only delete last item of a menu'
  538.         self.menu.DeleteMenuItem(item)
  539.         del self.items[item-1]
  540.     
  541.     def addcheck(self, label, shortcut=None, callback=None):
  542.         return self.additem(label, shortcut, callback, 'check')
  543.     
  544.     def addradio(self, label, shortcut=None, callback=None):
  545.         return self.additem(label, shortcut, callback, 'radio')
  546.     
  547.     def addseparator(self):
  548.         self.menu.AppendMenu('(-')
  549.         self.items.append('', None, None, 'separator')
  550.     
  551.     def addsubmenu(self, label, title=''):
  552.         sub = Menu(self.bar, title, -1)
  553.         item = self.additem(label, '\x1B', None, 'submenu')
  554.         self.menu.SetItemMark(item, sub.id)
  555.         sub._parent = self
  556.         sub._parent_item = item
  557.         return sub
  558.     
  559.     def dispatch(self, id, item, window, event):
  560.         title, shortcut, callback, mtype = self.items[item-1]
  561.         if callback:
  562.             if not self.bar.parent or type(callback) <> types.StringType:
  563.                 menuhandler = callback
  564.             else: 
  565.                 # callback is string
  566.                 wid = Win.FrontWindow()
  567.                 if wid and self.bar.parent._windows.has_key(wid):
  568.                     window = self.bar.parent._windows[wid]
  569.                     if hasattr(window, "domenu_" + callback):
  570.                         menuhandler = getattr(window, "domenu_" + callback)
  571.                     elif hasattr(self.bar.parent, "domenu_" + callback):
  572.                         menuhandler = getattr(self.bar.parent, "domenu_" + callback)
  573.                     else:
  574.                         # nothing we can do. we shouldn't have come this far
  575.                         # since the menu item should have been disabled...
  576.                         return
  577.                 elif hasattr(self.bar.parent, "domenu_" + callback):
  578.                     menuhandler = getattr(self.bar.parent, "domenu_" + callback)
  579.                 else:
  580.                     # nothing we can do. we shouldn't have come this far
  581.                     # since the menu item should have been disabled...
  582.                     return
  583.             menuhandler(id, item, window, event)
  584.  
  585.     def enable(self, onoff):
  586.         if onoff:
  587.             self.menu.EnableItem(0)
  588.             if self._parent:
  589.                 self._parent.menu.EnableItem(self._parent_item)
  590.         else:
  591.             self.menu.DisableItem(0)
  592.             if self._parent:
  593.                 self._parent.menu.DisableItem(self._parent_item)
  594.         if self.bar and self.bar.parent:
  595.                 self.bar.parent.needmenubarredraw = 1
  596.             
  597. class PopupMenu(Menu):
  598.     def __init__(self, bar):
  599.         Menu.__init__(self, bar, '(popup)', -1)
  600.         
  601.     def popup(self, x, y, event, default=1, window=None):
  602.         # NOTE that x and y are global coordinates, and they should probably
  603.         # be topleft of the button the user clicked (not mouse-coordinates),
  604.         # so the popup nicely overlaps.
  605.         reply = self.menu.PopUpMenuSelect(x, y, default)
  606.         if not reply:
  607.             return
  608.         id = (reply & 0xffff0000) >> 16
  609.         item = reply & 0xffff
  610.         if not window:
  611.             wid = Win.FrontWindow()
  612.             try:
  613.                 window = self.bar.parent._windows[wid]
  614.             except:
  615.                 pass # If we can't find the window we pass None
  616.         self.dispatch(id, item, window, event)
  617.  
  618. class MenuItem:
  619.     def __init__(self, menu, title, shortcut=None, callback=None, kind=None):
  620.         self.item = menu.additem(title, shortcut, callback)
  621.         self.menu = menu
  622.         
  623.     def delete(self):
  624.         self.menu.delitem(self.item)
  625.         del self.menu
  626.         del self.item
  627.         
  628.     def check(self, onoff):
  629.         self.menu.menu.CheckItem(self.item, onoff)
  630.  
  631.     def enable(self, onoff):
  632.         if onoff:
  633.             self.menu.menu.EnableItem(self.item)
  634.         else:
  635.             self.menu.menu.DisableItem(self.item)
  636.             
  637.     def settext(self, text):
  638.         self.menu.menu.SetMenuItemText(self.item, text)
  639.         
  640.     def setstyle(self, style):
  641.         self.menu.menu.SetItemStyle(self.item, style)
  642.         
  643.     def seticon(self, icon):
  644.         self.menu.menu.SetItemIcon(self.item, icon)
  645.         
  646.     def setcmd(self, cmd):
  647.         self.menu.menu.SetItemCmd(self.item, cmd)
  648.         
  649.     def setmark(self, cmd):
  650.         self.menu.menu.SetItemMark(self.item, cmd)
  651.         
  652.  
  653. class RadioItem(MenuItem):
  654.     def __init__(self, menu, title, shortcut=None, callback=None):
  655.         MenuItem.__init__(self, menu, title, shortcut, callback, 'radio')
  656.  
  657. class CheckItem(MenuItem):
  658.     def __init__(self, menu, title, shortcut=None, callback=None):
  659.         MenuItem.__init__(self, menu, title, shortcut, callback, 'check')
  660.  
  661. def Separator(menu):
  662.     menu.addseparator()
  663.  
  664. def SubMenu(menu, label, title=''):
  665.     return menu.addsubmenu(label, title)
  666.  
  667.  
  668. class AppleMenu(Menu):
  669.     
  670.     def __init__(self, bar, abouttext="About me...", aboutcallback=None):
  671.         Menu.__init__(self, bar, "\024")
  672.         self.additem(abouttext, None, aboutcallback)
  673.         self.addseparator()
  674.         self.menu.AppendResMenu('DRVR')
  675.     
  676.     def dispatch(self, id, item, window, event):
  677.         if item == 1:
  678.             Menu.dispatch(self, id, item, window, event)
  679.         else:
  680.             name = self.menu.GetMenuItemText(item)
  681.             OpenDeskAcc(name)
  682.  
  683. class Window:
  684.     """A single window belonging to an application"""
  685.     
  686.     def __init__(self, parent):
  687.         self.wid = None
  688.         self.parent = parent
  689.         
  690.     def open(self, bounds=(40, 40, 400, 400), resid=None):
  691.         if resid <> None:
  692.             self.wid = GetNewWindow(resid, -1)
  693.         else:
  694.             self.wid = NewWindow(bounds, self.__class__.__name__, 1,
  695.                 8, -1, 1, 0)    # changed to proc id 8 to include zoom box. jvr
  696.         self.do_postopen()
  697.         
  698.     def do_postopen(self):
  699.         """Tell our parent we exist"""
  700.         self.parent.appendwindow(self.wid, self)
  701.         
  702.     def close(self):
  703.         self.do_postclose()
  704.             
  705.     def do_postclose(self):
  706.         self.parent.removewindow(self.wid)
  707.         self.parent = None
  708.         self.wid = None
  709.         
  710.     def SetPort(self):
  711.         # Convinience method
  712.         SetPort(self.wid)
  713.     
  714.     def do_inDrag(self, partcode, window, event):
  715.         where = event[3]
  716.         window.DragWindow(where, self.draglimit)
  717.     
  718.     draglimit = screenbounds
  719.     
  720.     def do_inGoAway(self, partcode, window, event):
  721.         where = event[3]
  722.         if window.TrackGoAway(where):
  723.             self.close()
  724.     
  725.     def do_inZoom(self, partcode, window, event):
  726.         (what, message, when, where, modifiers) = event
  727.         if window.TrackBox(where, partcode):
  728.             window.ZoomWindow(partcode, 1)
  729.             rect = window.GetWindowUserState()                # so that zoom really works... jvr
  730.             self.do_postresize(rect[2] - rect[0], rect[3] - rect[1], window)    # jvr
  731.     
  732.     def do_inZoomIn(self, partcode, window, event):
  733.         SetPort(window) # !!!
  734.         self.do_inZoom(partcode, window, event)
  735.     
  736.     def do_inZoomOut(self, partcode, window, event):
  737.         SetPort(window) # !!!
  738.         self.do_inZoom(partcode, window, event)
  739.     
  740.     def do_inGrow(self, partcode, window, event):
  741.         (what, message, when, where, modifiers) = event
  742.         result = window.GrowWindow(where, self.growlimit)
  743.         if result:
  744.             height = (result>>16) & 0xffff    # Hi word
  745.             width = result & 0xffff        # Lo word
  746.             self.do_resize(width, height, window)
  747.     
  748.     growlimit = (50, 50, screenbounds[2] - screenbounds[0], screenbounds[3] - screenbounds[1])    # jvr
  749.     
  750.     def do_resize(self, width, height, window):
  751.         l, t, r, b = self.wid.GetWindowPort().portRect            # jvr, forGrowIcon
  752.         self.SetPort()                            # jvr
  753.         InvalRect((r - SCROLLBARWIDTH + 1, b - SCROLLBARWIDTH + 1, r, b))    # jvr
  754.         window.SizeWindow(width, height, 1)        # changed updateFlag to true jvr
  755.         self.do_postresize(width, height, window)
  756.     
  757.     def do_postresize(self, width, height, window):
  758.         SetPort(window)
  759.         InvalRect(window.GetWindowPort().portRect)
  760.     
  761.     def do_inContent(self, partcode, window, event):
  762.         #
  763.         # If we're not frontmost, select ourselves and wait for
  764.         # the activate event.
  765.         #
  766.         if FrontWindow() <> window:
  767.             window.SelectWindow()
  768.             return
  769.         # We are. Handle the event.
  770.         (what, message, when, where, modifiers) = event
  771.         SetPort(window)
  772.         local = GlobalToLocal(where)
  773.         self.do_contentclick(local, modifiers, event)
  774.         
  775.     def do_contentclick(self, local, modifiers, event):
  776.         if DEBUG:
  777.             print 'Click in contents at %s, modifiers %s'%(local, modifiers)
  778.     
  779.     def do_rawupdate(self, window, event):
  780.         if DEBUG: print "raw update for", window
  781.         SetPort(window)
  782.         window.BeginUpdate()
  783.         self.do_update(window, event)
  784.         window.EndUpdate()
  785.     
  786.     def do_update(self, window, event):
  787.         if DEBUG:
  788.             import time
  789.             for i in range(8):
  790.                 time.sleep(0.1)
  791.                 InvertRgn(window.GetWindowPort().visRgn)
  792.             FillRgn(window.GetWindowPort().visRgn, qd.gray)
  793.         else:
  794.             EraseRgn(window.GetWindowPort().visRgn)
  795.         
  796.     def do_activate(self, activate, event):
  797.         if DEBUG: print 'Activate %d for %s'%(activate, self.wid)
  798.         
  799. class ControlsWindow(Window):
  800.  
  801.     def do_rawupdate(self, window, event):
  802.         if DEBUG: print "raw update for", window
  803.         SetPort(window)
  804.         window.BeginUpdate()
  805.         self.do_update(window, event)
  806.         #DrawControls(window)                    # jvr
  807.         UpdateControls(window, window.GetWindowPort().visRgn)    # jvr
  808.         window.DrawGrowIcon()
  809.         window.EndUpdate()
  810.     
  811.     def do_controlhit(self, window, control, pcode, event):
  812.         if DEBUG: print "control hit in", window, "on", control, "; pcode =", pcode
  813.  
  814.     def do_inContent(self, partcode, window, event):
  815.         if FrontWindow() <> window:
  816.             window.SelectWindow()
  817.             return
  818.         (what, message, when, where, modifiers) = event
  819.         SetPort(window)  # XXXX Needed?
  820.         local = GlobalToLocal(where)
  821.         pcode, control = FindControl(local, window)
  822.         if pcode and control:
  823.             self.do_rawcontrolhit(window, control, pcode, local, event)
  824.         else:
  825.             if DEBUG: print "FindControl(%s, %s) -> (%s, %s)" % \
  826.                 (local, window, pcode, control)
  827.             self.do_contentclick(local, modifiers, event)
  828.             
  829.     def do_rawcontrolhit(self, window, control, pcode, local, event):
  830.         pcode = control.TrackControl(local)
  831.         if pcode:
  832.             self.do_controlhit(window, control, pcode, event)
  833.             
  834. class ScrolledWindow(ControlsWindow):
  835.     def __init__(self, parent):
  836.         self.barx = self.bary = None
  837.         self.barx_enabled = self.bary_enabled = 1
  838.         self.activated = 1
  839.         ControlsWindow.__init__(self, parent)
  840.  
  841.     def scrollbars(self, wantx=1, wanty=1):
  842.         SetPort(self.wid)
  843.         self.barx = self.bary = None
  844.         self.barx_enabled = self.bary_enabled = 1
  845.         x0, y0, x1, y1 = self.wid.GetWindowPort().portRect
  846.         vx, vy = self.getscrollbarvalues()
  847.         if vx == None: self.barx_enabled, vx = 0, 0
  848.         if vy == None: self.bary_enabled, vy = 0, 0
  849.         if wantx:
  850.             rect = x0-1, y1-(SCROLLBARWIDTH-1), x1-(SCROLLBARWIDTH-2), y1+1
  851.             self.barx = NewControl(self.wid, rect, "", 1, vx, 0, 32767, 16, 0)
  852.             if not self.barx_enabled: self.barx.HiliteControl(255)
  853. ##            InvalRect(rect)
  854.         if wanty:
  855.             rect = x1-(SCROLLBARWIDTH-1), y0-1, x1+1, y1-(SCROLLBARWIDTH-2)
  856.             self.bary = NewControl(self.wid, rect, "", 1, vy, 0, 32767, 16, 0)
  857.             if not self.bary_enabled: self.bary.HiliteControl(255)
  858. ##            InvalRect(rect)
  859.             
  860.     def do_postclose(self):
  861.         self.barx = self.bary = None
  862.         ControlsWindow.do_postclose(self)
  863.         
  864.     def do_activate(self, onoff, event):
  865.         self.activated = onoff
  866.         if onoff:
  867.             if self.barx and self.barx_enabled:
  868.                 self.barx.ShowControl()    # jvr
  869.             if self.bary and self.bary_enabled:
  870.                 self.bary.ShowControl()    # jvr
  871.         else:
  872.             if self.barx:
  873.                 self.barx.HideControl()    # jvr; An inactive window should have *hidden*
  874.                             # scrollbars, not just dimmed (no matter what
  875.                             # BBEdit does... look at the Finder)
  876.             if self.bary:
  877.                 self.bary.HideControl()    # jvr
  878.         self.wid.DrawGrowIcon()            # jvr
  879.             
  880.     def do_postresize(self, width, height, window):
  881.         l, t, r, b = self.wid.GetWindowPort().portRect
  882.         self.SetPort()
  883.         if self.barx:
  884.             self.barx.HideControl()        # jvr
  885.             self.barx.MoveControl(l-1, b-(SCROLLBARWIDTH-1))
  886.             self.barx.SizeControl((r-l)-(SCROLLBARWIDTH-3), SCROLLBARWIDTH)    # jvr
  887.         if self.bary:
  888.             self.bary.HideControl()        # jvr
  889.             self.bary.MoveControl(r-(SCROLLBARWIDTH-1), t-1)
  890.             self.bary.SizeControl(SCROLLBARWIDTH, (b-t)-(SCROLLBARWIDTH-3))    # jvr
  891.         if self.barx:
  892.             self.barx.ShowControl()        # jvr
  893.             ValidRect((l, b - SCROLLBARWIDTH + 1, r - SCROLLBARWIDTH + 2, b))    # jvr
  894.         if self.bary:
  895.             self.bary.ShowControl()        # jvr
  896.             ValidRect((r - SCROLLBARWIDTH + 1, t, r, b - SCROLLBARWIDTH + 2))    # jvr
  897.         InvalRect((r - SCROLLBARWIDTH + 1, b - SCROLLBARWIDTH + 1, r, b))    # jvr, growicon
  898.  
  899.             
  900.     def do_rawcontrolhit(self, window, control, pcode, local, event):
  901.         if control == self.barx:
  902.             which = 'x'
  903.         elif control == self.bary:
  904.             which = 'y'
  905.         else:
  906.             return 0
  907.         if pcode in (inUpButton, inDownButton, inPageUp, inPageDown):
  908.             # We do the work for the buttons and grey area in the tracker
  909.             dummy = control.TrackControl(local, self.do_controltrack)
  910.         else:
  911.             # but the thumb is handled here
  912.             pcode = control.TrackControl(local)
  913.             if pcode == inThumb:
  914.                 value = control.GetControlValue()
  915.                 print 'setbars', which, value #DBG
  916.                 self.scrollbar_callback(which, 'set', value)
  917.                 self.updatescrollbars()
  918.             else:
  919.                 print 'funny part', pcode #DBG
  920.         return 1
  921.         
  922.     def do_controltrack(self, control, pcode):
  923.         if control == self.barx:
  924.             which = 'x'
  925.         elif control == self.bary:
  926.             which = 'y'
  927.         else:
  928.             return
  929.  
  930.         if pcode == inUpButton:
  931.             what = '-'
  932.         elif pcode == inDownButton:
  933.             what = '+'
  934.         elif pcode == inPageUp:
  935.             what = '--'
  936.         elif pcode == inPageDown:
  937.             what = '++'
  938.         else:
  939.             return
  940.         self.scrollbar_callback(which, what, None)
  941.         self.updatescrollbars()
  942.         
  943.     def updatescrollbars(self):
  944.         SetPort(self.wid)
  945.         vx, vy = self.getscrollbarvalues()
  946.         if self.barx:
  947.             if vx == None:
  948.                 self.barx.HiliteControl(255)
  949.                 self.barx_enabled = 0
  950.             else:
  951.                 if not self.barx_enabled:
  952.                     self.barx_enabled = 1
  953.                     if self.activated:
  954.                         self.barx.HiliteControl(0)
  955.                 self.barx.SetControlValue(vx)
  956.         if self.bary:
  957.             if vy == None:
  958.                 self.bary.HiliteControl(255)
  959.                 self.bary_enabled = 0
  960.             else:
  961.                 if not self.bary_enabled:
  962.                     self.bary_enabled = 1
  963.                     if self.activated:
  964.                         self.bary.HiliteControl(0)
  965.                 self.bary.SetControlValue(vy)
  966.             
  967.     # Auxiliary function: convert standard text/image/etc coordinate
  968.     # to something palatable as getscrollbarvalues() return
  969.     def scalebarvalue(self, absmin, absmax, curmin, curmax):
  970.         if curmin <= absmin and curmax >= absmax:
  971.             return None
  972.         if curmin <= absmin:
  973.             return 0
  974.         if curmax >= absmax:
  975.             return 32767
  976.         perc = float(curmin-absmin)/float(absmax-absmin)
  977.         return int(perc*32767)
  978.             
  979.     # To be overridden:
  980.     
  981.     def getscrollbarvalues(self):
  982.         return 0, 0
  983.         
  984.     def scrollbar_callback(self, which, what, value):
  985.         print 'scroll', which, what, value
  986.     
  987. class DialogWindow(Window):
  988.     """A modeless dialog window"""
  989.     
  990.     def open(self, resid):
  991.         self.wid = GetNewDialog(resid, -1)
  992.         self.do_postopen()
  993.         
  994.     def close(self):
  995.         self.do_postclose()
  996.         
  997.     def do_itemhit(self, item, event):
  998.         print 'Dialog %s, item %d hit'%(self.wid, item)
  999.         
  1000.     def do_rawupdate(self, window, event):
  1001.         pass
  1002.  
  1003. def ostypecode(x):
  1004.     "Convert a long int to the 4-character code it really is"
  1005.     s = ''
  1006.     for i in range(4):
  1007.         x, c = divmod(x, 256)
  1008.         s = chr(c) + s
  1009.     return s
  1010.  
  1011.  
  1012. class TestApp(Application):
  1013.     
  1014.     "This class is used by the test() function"
  1015.     
  1016.     def makeusermenus(self):
  1017.         self.filemenu = m = Menu(self.menubar, "File")
  1018.         self.saveitem = MenuItem(m, "Save", "S", self.save)
  1019.         Separator(m)
  1020.         self.optionsmenu = mm = SubMenu(m, "Options")
  1021.         self.opt1 = CheckItem(mm, "Arguments")
  1022.         self.opt2 = CheckItem(mm, "Being hit on the head lessons")
  1023.         self.opt3 = CheckItem(mm, "Complaints")
  1024.         Separator(m)
  1025.         self.quititem = MenuItem(m, "Quit", "Q", self.quit)
  1026.     
  1027.     def save(self, *args):
  1028.         print "Save"
  1029.     
  1030.     def quit(self, *args):
  1031.         raise self
  1032.  
  1033.  
  1034. def test():
  1035.     "Test program"
  1036.     app = TestApp()
  1037.     app.mainloop()
  1038.  
  1039.  
  1040. if __name__ == '__main__':
  1041.     test()
  1042.